﻿using MicroApi.Core.Filters;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

using Newtonsoft.Json.Serialization;

using System;
using System.IO;
using System.Linq;
using System.Reflection;

using Utils.JobTask;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MicroApiServicesExtensions
    {
        public static IServiceCollection ConfigureWritable<T>(this IServiceCollection services, IConfigurationSection section, string fileName = "appsettings") where T : class, new()
        {
            services.Configure<T>(section);
            services.AddTransient<IWritableOptions<T>>(provider =>
            {
                var environment = provider.GetService<IWebHostEnvironment>();
                var options = provider.GetService<IOptionsMonitor<T>>();
                return new WritableOptions<T>(environment, options, section.Key, fileName);
            });
            return services;
        }
        public static IServiceCollection AddUtilsDefault(this IServiceCollection services)
        {
            services.AddJob();
            services.AddMemoryCache();
            services.AddInjection();
            services.AddLazyService();
            services.AddLogging();
            return services;
        }
        /// <summary>
        /// 添加默认配置
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IMvcBuilder AddApiConfig(this IServiceCollection services)
        {
            return services
                 .AddControllers(options =>
                 {
                     options.Filters.Add<AuthorizationFilter>();
                     options.Filters.Add<ExceptionFilter>();
                     options.Filters.Add<ActionResultFilter>();
                 })
                 .AddNewtonsoftJson(options =>
                 {
                     options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                     options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
                     options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                 })
                 .ConfigureApiBehaviorOptions(options =>
                 {
                     options.SuppressModelStateInvalidFilter = true;
                 });
        }

        public static IServiceCollection AddAuth<T>(this IServiceCollection services) where T : AuthIdentity
        {
            return services.AddScoped<AuthIdentity, T>();
        }



        public static ISignalRServerBuilder AddSignalRConfig(this IServiceCollection services)
        {

            return services
               .AddSignalR(options =>
               {
                   options.EnableDetailedErrors = true;
               })
               .AddNewtonsoftJsonProtocol(options =>
               {
                   options.PayloadSerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                   options.PayloadSerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
                   options.PayloadSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
               });
        }

        /// <summary>
        /// 使用默认配置
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseApiConfig(this IApplicationBuilder app)
        {
            IWebHostEnvironment env = app.ApplicationServices.GetService<IWebHostEnvironment>();
            var webRootPath = app.ApplicationServices.GetService<IConfiguration>().GetValue<string>("WebRootPath");
            if (!string.IsNullOrEmpty(webRootPath))
                env.WebRootPath = Path.Combine(env.ContentRootPath, webRootPath);
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStatusCodePages();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                AppendHub(endpoints);
                endpoints.MapControllers();
            });
            return app;
        }


        /// <summary>
        /// 启用任务
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseJob(this IApplicationBuilder app)
        {
            return app.UseJob(null);
        }

        /// <summary>
        /// 启用任务
        /// </summary>
        /// <param name="app"></param>
        /// <param name="configJobs"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseJob(this IApplicationBuilder app, Action<JobTaskBuilder> configJobs)
        {
            var jobService = app.ApplicationServices.GetRequiredService<IJobProvider>();
            JobTaskBuilder jobBuilder = new JobTaskBuilder();
            configJobs?.Invoke(jobBuilder);
            jobService.Run(jobBuilder);
            return app;
        }

        private static void AppendHub(IEndpointRouteBuilder endpointRouteBuilder)
        {
            var hubType = typeof(Hub);
            var types = Assembly.GetEntryAssembly().GetTypes().Where(a => hubType.IsAssignableFrom(a)).ToArray();
            var method = typeof(HubEndpointRouteBuilderExtensions).GetMethods().Where(a => a.Name == "MapHub" && a.GetParameters().Length == 2).FirstOrDefault();
            foreach (var type in types)
            {
                var route = type.GetCustomAttribute<RouteAttribute>();
                if (route == null) continue;
                MapHub(method, endpointRouteBuilder, type, route);
            }
        }
        private static void MapHub(MethodInfo methodInfo, IEndpointRouteBuilder endpointRouteBuilder, Type type, RouteAttribute route)
        {
            string path = route.Template;
            if (path.Contains("[hub]"))
            {
                string name = type.Name.ToLower();
                path = path.Replace("[hub]", name.EndsWith("hub") ? name.Substring(0, name.Length - 3) : name);
            }
            methodInfo.MakeGenericMethod(type).Invoke(null, new object[] { endpointRouteBuilder, path });
        }
    }
}
